#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>


#define KEY_CNT         1      /* 设备号个数   */
#define KEY_NAME      "keyirq"    /* 名字         */
#define KEY_NUM         2
/* 定义按键值 */
#define KEY1VALUE      0X01    /* 按键值       */
#define KEY2VALUE      0X02    /* 按键值       */
#define INVAKEY        0X00    /* 无效的irq按键值 */

struct irq_Keydesc{
    int gpio;       /* IO编号 */
    int irqnum;     /* 中断号*/
    unsigned char value; /* 键值*/
    char name[10];      /* 名字*/
    irqreturn_t (*handler)(int, void *);  /* 中断处理函数*/
};

/* key设备结构体 */
struct irqKeyDevStruct{
   dev_t         devid;      /* 设备号              */
   struct cdev   cdev;       /* cdev                */
   struct class  *class;     /* 类                  */
   struct device *device;    /* 设备                */
   int           major;      /* 主设备号            */
   int           minor;      /* 次设备号            */
   struct device_node *nd;   /* 设备节点            */
   struct irq_Keydesc irqKeydescCfg[KEY_NUM]; /* 按键结构体 */
   struct  timer_list timer1;
   struct  timer_list timer2; 
   atomic_t      keyvalue;   /* 按键的值            */
   atomic_t      releaseKey;
};

struct irqKeyDevStruct irqKeyDev;       /* key设备 */

/* @description    : 中断服务函数，开启定时器，延时10ms，
 *                   定时器用于按键消抖。
 * @param - irq    : 中断号 
 * @param - dev_id : 设备结构。
 * @return         : 中断执行结果
 */
static irqreturn_t key1_handler(int irq, void *dev_id)
{
    struct irqKeyDevStruct *dev = (struct irqKeyDevStruct *)dev_id;

    dev->timer1.data = (volatile long)dev_id;
    mod_timer(&dev->timer1, jiffies + msecs_to_jiffies(10)); /* 10ms定时 */
    
    return IRQ_RETVAL(IRQ_HANDLED);
}

static irqreturn_t key2_handler(int irq, void *dev_id)
{
    struct irqKeyDevStruct *dev = (struct irqKeyDevStruct*)dev_id;

    dev->timer2.data = (volatile long)dev_id;
    mod_timer(&dev->timer2, jiffies + msecs_to_jiffies(10)); /* 10ms定时 */
    
    return IRQ_RETVAL(IRQ_HANDLED);
}

/* @description : 定时器服务函数，用于按键消抖，定时器到了以后
 *                再次读取按键值，如果按键还是处于按下状态就表示按键有效。
 * @param - arg : 设备结构变量
 * @return      : 无
 */
void timer1_function(unsigned long arg)
{
    unsigned char value;
    struct irq_Keydesc *keydesc;
    struct irqKeyDevStruct *dev = (struct irqKeyDevStruct  *)arg;

    keydesc = &dev->irqKeydescCfg[0];

    value = gpio_get_value(keydesc->gpio); /* 读取IO值 */
    if(value == 1) /* 按下按键 */
    {
        printk("get key1: high\r\n");
        atomic_set(&dev->keyvalue, keydesc->value);
    }
    else /* 按键松开 */
    {
        printk("key1 release\r\n");
        atomic_set(&dev->keyvalue, 0x80 | keydesc->value);
        atomic_set(&dev->releaseKey, 1); /* 标记松开按键，即完成一次完整的按键过程 */            
    }    
}

void timer2_function(unsigned long arg)
{
      unsigned char value;
    struct irq_Keydesc *keydesc;
    struct irqKeyDevStruct  *dev = (struct irqKeyDevStruct  *)arg;

    keydesc = &dev->irqKeydescCfg[1];

    value = gpio_get_value(keydesc->gpio); /* 读取IO值 */
    if(value == 0) /* 按下按键 */
    {
        printk("get key2: low\r\n");
        atomic_set(&dev->keyvalue, keydesc->value);
    }
    else /* 按键松开 */
    {
        printk("key2 release\r\n");
        atomic_set(&dev->keyvalue, 0x80 | keydesc->value);
        atomic_set(&dev->releaseKey, 1); /* 标记松开按键，即完成一次完整的按键过程 */            
    }    
}

/*
 * @description   : 初始化按键IO，open函数打开驱动的时候
 *                  初始化按键所使用的GPIO引脚。
 * @param         : 无
 * @return        : 无
 */
static int keyio_init(void)
{   
    int ret = 0;
    char gpio_name[15];
    irqKeyDev.nd = of_find_node_by_path("/swkey");
    if (irqKeyDev.nd== NULL) 
    {
        return -EINVAL;
    }

    for(int i=0 ;i<KEY_NUM; i++)
    {   
        sprintf(gpio_name,"key%d-gpio",i+1);
        irqKeyDev.irqKeydescCfg[i].gpio = of_get_named_gpio(irqKeyDev.nd,gpio_name,0);
        if(irqKeyDev.irqKeydescCfg[i].gpio < 0)
        {
            printk("can't get key%d\r\n", i);
        }
    }

   
    if ((irqKeyDev.irqKeydescCfg[0].gpio < 0)||(irqKeyDev.irqKeydescCfg[1].gpio < 0))
    {
        printk("can't get key\r\n");
        return -EINVAL;
    }
    printk("key1_gpio=%d, key2_gpio=%d\r\n", irqKeyDev.irqKeydescCfg[0].gpio,irqKeyDev.irqKeydescCfg[1].gpio  );

    // /* 初始化key所使用的IO */
    // gpio_request(irqKeyDev.irqKeydescCfg[0].gpio , "key1");    /* 请求IO */
    // gpio_request(irqKeyDev.irqKeydescCfg[1].gpio , "key2");    /* 请求IO */
    // gpio_direction_input(irqKeyDev.irqKeydescCfg[0].gpio );    /* 设置为输入 */
    // gpio_direction_input(irqKeyDev.irqKeydescCfg[1].gpio );    /* 设置为输入 */
    /* 初始化key所使用的 并设置为中断模式*/
    for(int i=0;i<KEY_NUM;i++)
    {
        memset(irqKeyDev.irqKeydescCfg[i].name,0,sizeof(irqKeyDev.irqKeydescCfg[i].name));   //缓冲区清零
        sprintf(irqKeyDev.irqKeydescCfg[i].name, "key%d", i+1);        /* 组合名字 */
        gpio_request(irqKeyDev.irqKeydescCfg[i].gpio, irqKeyDev.irqKeydescCfg[i].name);
        gpio_direction_input(irqKeyDev.irqKeydescCfg[i].gpio);    
        irqKeyDev.irqKeydescCfg[i].irqnum = irq_of_parse_and_map(irqKeyDev.nd, i);

        printk("key%d:gpio=%d, irqnum=%d\r\n",i+1,
                                            irqKeyDev.irqKeydescCfg[i].gpio, 
                                            irqKeyDev.irqKeydescCfg[i].irqnum);
    }

    irqKeyDev.irqKeydescCfg[0].handler = key1_handler;
    irqKeyDev.irqKeydescCfg[1].handler = key2_handler;
    irqKeyDev.irqKeydescCfg[0].value = KEY1VALUE;
    irqKeyDev.irqKeydescCfg[1].value = KEY2VALUE;
    
    for (int i = 0; i < KEY_NUM; i++)
    {
        ret = request_irq(irqKeyDev.irqKeydescCfg[i].irqnum,
                          irqKeyDev.irqKeydescCfg[i].handler,
                          IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, //下降沿 上升沿触发
                          irqKeyDev.irqKeydescCfg[i].name,
                          &irqKeyDev);
        if(ret < 0)
        {
            printk("irq %d request failed!\r\n", irqKeyDev.irqKeydescCfg[i].irqnum);
            return -EFAULT;
        }
    }

    /* 创建定时器 */
    init_timer(&irqKeyDev.timer1);
    irqKeyDev.timer1.function = timer1_function;
    init_timer(&irqKeyDev.timer2);
    irqKeyDev.timer2.function = timer2_function;
    return 0;
}

/*
 * @description      : 打开设备
 * @param - inode    : 传递给驱动的inode
 * @param - filp     : 设备文件，file结构体有个叫做private_data的成员变量
 *                     一般在open的时候将private_data指向设备结构体。
 * @return           : 0 成功;其他 失败
 */
static int key_open(struct inode *inode, struct file *filp)
{
    int ret = 0;
    filp->private_data = &irqKeyDev;    /* 设置私有数据 */

    ret = keyio_init();              /* 初始化按键IO */
    if (ret < 0)
    {
        return ret;
    }

    return 0;
}

/*
 * @description     : 从设备读取数据 
 * @param - filp    : 要打开的设备文件(文件描述符)
 * @param - buf     : 返回给用户空间的数据缓冲区
 * @param - cnt     : 要读取的数据长度
 * @param - offt    : 相对于文件首地址的偏移
 * @return          : 读取的字节数，如果为负值，表示读取失败
 */
static ssize_t key_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    int ret = 0;
    
    unsigned char keyvalue = 0;
    unsigned char releaseKey = 0;

    struct irqKeyDevStruct *dev=(struct irqKeyDevStruct*)filp->private_data;

    keyvalue = atomic_read(&dev->keyvalue);
    releaseKey = atomic_read(&dev->releaseKey);

    if(releaseKey !=0 )
    {
        if(keyvalue & 0x80)
        {
            keyvalue &= ~0x80;
            ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
        }
       else
      {
        return EINVAL;
      }   
        atomic_set(&dev->releaseKey, 0); /* 按下标志清零 */
    }
    
    return ret;
}

/*
 * @description     : 向设备写数据 
 * @param - filp    : 设备文件，表示打开的文件描述符
 * @param - buf     : 要写给设备写入的数据
 * @param - cnt     : 要写入的数据长度
 * @param - offt    : 相对于文件首地址的偏移
 * @return          : 写入的字节数，如果为负值，表示写入失败
 */
static ssize_t key_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    return 0;
}

/*
 * @description     : 关闭/释放设备
 * @param - filp    : 要关闭的设备文件(文件描述符)
 * @return          : 0 成功;其他 失败
 */
static int key_release(struct inode *inode, struct file *filp)
{
    return 0;
}

/* 设备操作函数 */
static struct file_operations key_fops = {
    .owner   = THIS_MODULE,
    .open    = key_open,
    .read    = key_read,
    .write   = key_write,
    .release = key_release,
};

/*
 * @description  : 驱动入口函数
 * @param        : 无
 * @return       : 无
 */
static int __init mykey_init(void)
{
    /* 初始化原子变量 */
    atomic_set(&irqKeyDev.keyvalue, INVAKEY);
    atomic_set(&irqKeyDev.releaseKey,0);
    /* 注册字符设备驱动 */
    /* 1、创建设备号 */
    if (irqKeyDev.major) /*  定义了设备号 */
    {
        irqKeyDev.devid = MKDEV(irqKeyDev.major, 0);
        register_chrdev_region(irqKeyDev.devid, KEY_CNT, KEY_NAME);
    }
    else /* 没有定义设备号 */
    {
        alloc_chrdev_region(&irqKeyDev.devid, 0, KEY_CNT, KEY_NAME);   /* 申请设备号 */
        irqKeyDev.major = MAJOR(irqKeyDev.devid);   /* 获取分配号的主设备号 */
        irqKeyDev.minor = MINOR(irqKeyDev.devid);   /* 获取分配号的次设备号 */
    }
    printk("irqKeyDev major=%d,minor=%d\n",irqKeyDev.major, irqKeyDev.minor);

    /* 2、初始化cdev */
    irqKeyDev.cdev.owner = THIS_MODULE;
    cdev_init(&irqKeyDev.cdev, &key_fops);

    /* 3、添加一个cdev */
    cdev_add(&irqKeyDev.cdev, irqKeyDev.devid, KEY_CNT);

    /* 4、创建类 */
    irqKeyDev.class = class_create(THIS_MODULE, KEY_NAME);
    if (IS_ERR(irqKeyDev.class))
    {
        return PTR_ERR(irqKeyDev.class);
    }

    /* 5、创建设备 */
    irqKeyDev.device = device_create(irqKeyDev.class, NULL, irqKeyDev.devid, NULL, KEY_NAME);
    if (IS_ERR(irqKeyDev.device))
    {
        return PTR_ERR(irqKeyDev.device);
    }

    printk("irqKeyDev init done!\n");
    return 0;
}

/*
 * @description  : 驱动出口函数
 * @param        : 无
 * @return       : 无
 */
static void __exit mykey_exit(void)
{   
    del_timer_sync(&irqKeyDev.timer1);
    del_timer_sync(&irqKeyDev.timer2);

    //释放申请的中断
     for (int i = 0; i < KEY_NUM; i++)
    {
        free_irq(irqKeyDev.irqKeydescCfg[i].irqnum, &irqKeyDev);
    }
    /* 注销字符设备驱动 */
    cdev_del(&irqKeyDev.cdev);/*  删除cdev */
    unregister_chrdev_region(irqKeyDev.devid, KEY_CNT); /* 注销设备号 */

    device_destroy(irqKeyDev.class, irqKeyDev.devid);
    class_destroy(irqKeyDev.class);
}

module_init(mykey_init);
module_exit(mykey_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zzlyang");
